﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace RobocopyGUI
{
    public partial class frmMain : Form
    {
        RobocopyJob job;
        public frmMain()
        {
            InitializeComponent();
            attrSwitchAm.SetAttributes("R,A,S,H,C,N,E,T");
            attrSwitchAp.SetAttributes("R,A,S,H,C,N,E,T");
            attrSwitchIA.SetAttributes("R,A,S,H,C,N,E,T,O");
            attrSwitchXA.SetAttributes("R,A,S,H,C,N,E,T,O");
            attrSwitchCopy.SetAttributes("D,A,T,S,O,U");
            lvwSwitchXF.Columns[0].Width = lvwSwitchXF.Width - 5;
            lvwSwitchXD.Columns[0].Width = lvwSwitchXD.Width - 5;
            /* create and instantiate the RobocopyJob object, which contains all the functionality pertaining
             * to the robocopy command that will be built and executed */
            job = new RobocopyJob(txtCommand);
            /* subscribe to the robocopy job's CommandChanged event, which fires on a timer two seconds after any
             * parameters are added or removed */
            job.CommandChanged += new RobocopyJob.CommandChangedHandler(job_CommandChanged);
            /* subscribe to the robocopy job's CommandCompleted event, which fires when the robocopy command
             * has finished */
            job.CommandCompleted += new RobocopyJob.CommandCompletedHandler(job_CommandCompleted);
        }

        void job_CommandCompleted(object sender, EventArgs e)
        {
            btnRun.Enabled = true;
            prgRunning.Visible = false;
        }

        /// <summary>
        /// Event handler for the RobocopyJob object's CommandChanged event, which fires two seconds after any
        /// parameters are added or removed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void job_CommandChanged(RobocopyJob sender, EventArgs e)
        {
            // display the current robocopy command in a textbox on the form.
            txtCommand.Text = job.Command;
        }

        /// <summary>
        /// Event handler for MouseOver on all switch checkboxes to display help for that switch.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void switchCheckBoxes_DisplayHelp(object sender, MouseEventArgs e)
        {
            /*  search the RobocopyJob object's parameter reference for the switch with a syntax matching the checkbox's
                text, and display the help message for that switch (contained in the Description property). ParamReference
                is a statically typed list of custom Param objects that contain information about every robocopy switch. */
            RobocopyJob.Param currentParameter = job.ParamReference.First(param => param.Syntax == ((CheckBox)sender).Text);
            strpHelp.Text = currentParameter.Description;
        }

        /// <summary>
        /// Event handler for check/uncheck on all checkboxes.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void switchCheckBoxes_CheckedChanged(object sender, EventArgs e)
        {
            CheckBox currentSwitch = (CheckBox)sender;
            // retrieve the current switch from the parameter reference
            RobocopyJob.Param currentParam = job.ParamReference.First(param => param.Syntax == currentSwitch.Text);

            /* for Switch types (on/off switches which do not require any values) lets go ahead and add the switch
             * to the command, since no other input is needed */
            if (currentParam.ParamType == RobocopyJob.ParamType.Switch) { job.ToggleSwitch(currentSwitch.Text); }

            /* some switches are mutually exclusive with other switches or groups of switches. also, the input controls
             * for parameters which require values should only be enabled when that switch is checked. all this
             * parameter-specific behavior goes below */
            switch (currentSwitch.Name)
            {
                #region /A+:
                // enable its associated attribute checkboxes only when it is checked
                case "chkSwitchAp":
                    attrSwitchAp.Enabled = chkSwitchAp.Checked;
                    break;
                #endregion
                #region /A-:
                // enable its associated attribute checkboxes only when it is checked
                case "chkSwitchAm":
                    attrSwitchAm.Enabled = chkSwitchAm.Checked;
                    break;
                #endregion
                #region /COPY
                // enable its associated attribute checkboxes only when it is checked
                case "chkSwitchCopy":
                    attrSwitchCopy.Enabled = chkSwitchCopy.Checked;
                    break;
                #endregion
                #region /COPYALL
                /* equivalent to /COPY:DATSOU and is mutually exclusive with /SEC, so disable those */
                case "chkSwitchCopyAll":
                    
                    chkSwitchCopy.Enabled = !chkSwitchCopyAll.Checked;
                    attrSwitchCopy.Enabled = !chkSwitchCopyAll.Checked;

                    chkSwitchSec.Enabled = !chkSwitchCopyAll.Checked;
                    break;
                #endregion
                #region /E
                // mutually exclusive with /S
                case "chkSwitchE":
                    chkSwitchS.Enabled = !chkSwitchE.Checked;
                    break; 
                #endregion
                #region /IA:
                // enable its associated attribute checkboxes only when it is checked
                case "chkSwitchIA":
                    attrSwitchIA.Enabled = chkSwitchIA.Checked;
                    break;
                #endregion
                #region /IF
                // enable and focus on the input controls for its value only when it is checked
                case "chkSwitchIF":
                    lvwSwitchIF.Enabled = chkSwitchIF.Checked;
                    btnSwitchIF.Enabled = chkSwitchIF.Checked;
                    break;
                #endregion
                #region /IPG:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchIPG":
                    numSwitchIPG.Enabled = chkSwitchIPG.Checked;
                    if (chkSwitchIPG.Checked) { numSwitchIPG.Focus(); }
                    break;
                #endregion
                #region /JOB:
                // enable and focus on the input controls for its value only when it is checked
                case "chkSwitchJob":
                    lvwSwitchJob.Enabled = chkSwitchJob.Checked;
                    btnSwitchJob.Enabled = chkSwitchJob.Checked;
                    break;
                #endregion
                #region /LEV:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchLEV":
                    numSwitchLEV.Enabled = chkSwitchLEV.Checked;
                    if (chkSwitchLEV.Checked) { numSwitchLEV.Focus(); }
                    break;
                #endregion
                #region /LOG:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchLog":
                    txtSwitchLog.Enabled = chkSwitchLog.Checked;
                    chkSwitchLogp.Enabled = !chkSwitchLog.Checked;
                    chkSwitchUnilog.Enabled = !chkSwitchLog.Checked;
                    chkSwitchUnilogp.Enabled = !chkSwitchLog.Checked;
                    break;
                #endregion
                #region /LOG+:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchLogp":
                    txtSwitchLogp.Enabled = chkSwitchLogp.Checked;
                    chkSwitchLog.Enabled = !chkSwitchLogp.Checked;
                    chkSwitchUnilog.Enabled = !chkSwitchLogp.Checked;
                    chkSwitchUnilogp.Enabled = !chkSwitchLogp.Checked;
                    break;
                #endregion
                #region /MAX:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchMax":
                    numSwitchMax.Enabled = chkSwitchMax.Checked;
                    if (chkSwitchMax.Checked) { numSwitchMax.Focus(); }
                    break;
                #endregion
                #region /MAXAGE:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchMaxAge":
                    numSwitchMaxAge.Enabled = chkSwitchMaxAge.Checked;
                    if (chkSwitchMaxAge.Checked) { numSwitchMaxAge.Focus(); }
                    break;
                #endregion
                #region /MAXLAD:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchMaxLAD":
                    numSwitchMaxLAD.Enabled = chkSwitchMaxLAD.Checked;
                    if (chkSwitchMaxLAD.Checked) { numSwitchMaxLAD.Focus(); }
                    break;
                #endregion
                #region /MIN:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchMin":
                    numSwitchMin.Enabled = chkSwitchMin.Checked;
                    if (chkSwitchMin.Checked) { numSwitchMin.Focus(); }
                    break;
                #endregion
                #region /MINAGE:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchMinAge":
                    numSwitchMinAge.Enabled = chkSwitchMinAge.Checked;
                    if (chkSwitchMinAge.Checked) { numSwitchMinAge.Focus(); }
                    break;
                #endregion
                #region /MINLAD:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchMinLAD":
                    numSwitchMinLAD.Enabled = chkSwitchMinLAD.Checked;
                    if (chkSwitchMinLAD.Checked) { numSwitchMinLAD.Focus(); }
                    break;
                #endregion
                #region /MIR
                // equivalent to /E /PURGE, so disable those
                case "chkSwitchMIR":
                    if (chkSwitchMIR.Checked)
                    {
                        chkSwitchE.Checked = true;
                        chkSwitchPurge.Checked = true;
                    }
                    else
                    {
                        chkSwitchPurge.Checked = false;
                    }
                    chkSwitchE.Enabled = !chkSwitchMIR.Checked;
                    chkSwitchPurge.Enabled = !chkSwitchMIR.Checked;
                    break;
                #endregion
                #region /MON:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchMON":
                    numSwitchMON.Enabled = chkSwitchMON.Checked;
                    if (chkSwitchMON.Checked) { numSwitchMON.Focus(); }
                    break;
                #endregion
                #region /MOT:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchMOT":
                    numSwitchMOT.Enabled = chkSwitchMOT.Checked;
                    if (chkSwitchMOT.Checked) { numSwitchMOT.Focus(); }
                    break;
                #endregion
                #region /MT:
                /* enable and focus on the input control for its value only when it is checked
                 * it is also mutually exclusive with /IPG: and /EFSRAW, so disable those when it is checked */
                case "chkSwitchMT":
                    numSwitchMT.Enabled = chkSwitchMT.Checked;
                    if (chkSwitchMT.Checked) { numSwitchMT.Focus(); }
                    chkSwitchIPG.Checked = false;
                    chkSwitchEFSRAW.Checked = false;
                    chkSwitchIPG.Enabled = !chkSwitchMT.Checked;
                    chkSwitchEFSRAW.Enabled = !chkSwitchMT.Checked;
                    break;
                #endregion
                #region /NOSD
                // disable the source input controls when checked
                case "chkSwitchNOSD":
                    txtSource.Enabled = !chkSwitchNOSD.Checked;
                    break;
                #endregion
                #region /NODD
                // disable the destination input controls when checked
                case "chkSwitchNODD":
                    txtTarget.Enabled = !chkSwitchNODD.Checked;
                    break;
                #endregion
                #region /R:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchR":
                    numSwitchR.Enabled = chkSwitchR.Checked;
                    break;
                #endregion
                #region /RH:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchRH":
                    mtxtSwitchRH.Enabled = chkSwitchRH.Checked;
                    break;
                #endregion
                #region /S
                // mutually exclusive with /E
                case "chkSwitchS":
                    chkSwitchE.Enabled = !chkSwitchS.Checked;
                    break;
                #endregion
                #region /SAVE:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchSave":
                    txtSwitchSave.Enabled = chkSwitchSave.Checked;
                    break;
                #endregion
                #region /SEC
                /* equivalent to /COPY:DATS, so check and disable the /COPY:DATS switches
                 * it is also mutually exclusive with /COPYALL */
                case "chkSwitchSec":
                    
                    chkSwitchCopy.Enabled = !chkSwitchSec.Checked;
                    attrSwitchCopy.Enabled = !chkSwitchSec.Checked;

                    chkSwitchCopyAll.Enabled = !chkSwitchSec.Checked;
                    break; 
                #endregion
                #region /UNILOG:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchUnilog":
                    txtSwitchUnilog.Enabled = chkSwitchUnilog.Checked;
                    chkSwitchLog.Enabled = !chkSwitchUnilog.Checked;
                    chkSwitchLogp.Enabled = !chkSwitchUnilog.Checked;
                    chkSwitchUnilogp.Enabled = !chkSwitchUnilog.Checked;
                    break;
                #endregion
                #region /UNILOG+:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchUnilogp":
                    txtSwitchUnilogp.Enabled = chkSwitchUnilogp.Checked;
                    chkSwitchLog.Enabled = !chkSwitchUnilogp.Checked;
                    chkSwitchLogp.Enabled = !chkSwitchUnilogp.Checked;
                    chkSwitchUnilog.Enabled = !chkSwitchUnilogp.Checked;
                    break;
                #endregion
                #region /W:
                // enable and focus on the input control for its value only when it is checked
                case "chkSwitchW":
                    numSwitchW.Enabled = chkSwitchW.Checked;
                    break;
                #endregion
                #region /XA:
                // enable its associated attribute checkboxes only when it is checked
                case "chkSwitchXA":
                    attrSwitchXA.Enabled = chkSwitchXA.Checked;
                    break;
                #endregion
                #region /XD
                // enable and focus on the input controls for its value only when it is checked
                case "chkSwitchXD":
                    lvwSwitchXD.Enabled = chkSwitchXD.Checked;
                    btnSwitchXD.Enabled = chkSwitchXD.Checked;
                    break;
                #endregion
                #region /XF
                // enable and focus on the input controls for its value only when it is checked
                case "chkSwitchXF":
                    lvwSwitchXF.Enabled = chkSwitchXF.Checked;
                    btnSwitchXF.Enabled = chkSwitchXF.Checked;
                    break; 
                #endregion
            }
        }

        /// <summary>
        /// Event handler for enable/disable on all checkboxes.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void switchCheckBoxes_EnabledChanged(object sender, EventArgs e)
        {
            CheckBox currentCheckBox = (CheckBox)sender;
            if (currentCheckBox.Checked) { job.ToggleSwitch(currentCheckBox.Text); }
        }

        private void btn_Click_BrowseFolder(object sender, EventArgs e)
        {
            switch (((Button)sender).Name)
            {
                case "btnSource":
                    dlgPickFolder.ShowNewFolderButton = false;
                    txtSource.Text = (dlgPickFolder.ShowDialog() == DialogResult.OK) ? dlgPickFolder.SelectedPath : "";
                    break;
                case "btnTarget":
                    txtTarget.Text = (dlgPickFolder.ShowDialog() == DialogResult.OK) ? dlgPickFolder.SelectedPath : "";
                    break;
                case "btnSwitchXD":
                    mnuSwitchXD.Show(btnSwitchXD, 0, btnSwitchXD.Height);
                    break;
            }
        }

        private void btn_Click_BrowseFile(object sender, EventArgs e)
        {
            Button currentButton = (Button)sender;
            switch (currentButton.Name)
            {
                case "btnFiles":
                    //txtFiles.Text += (dlgPickFile.ShowDialog() == DialogResult.OK) ? dlgPickFile.FileNames : "";
                    break;
                case "btnSwitchXF":
                    mnuSwitchXF.Show(btnSwitchXF, 0, btnSwitchXF.Height);
                    break;
                case "btnSwitchIF":
                case "btnSwitchJob":
                    DialogResult result = dlgPickFile.ShowDialog();
                    if (result == DialogResult.OK)
                    {
                        CheckBox currentSwitch = (CheckBox)this.Controls.Find(currentButton.Name.Replace("btn", "chk"), true)[0];
                        ListView currentList = (ListView)this.Controls.Find(currentButton.Name.Replace("btn", "lvw"), true)[0];
                        foreach (string file in dlgPickFile.FileNames)
                        {
                            currentList.Items.Add(file);
                        }
                        currentList.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
                        string[] values = new string[currentList.Items.Count];
                        foreach (ListViewItem item in currentList.Items)
                        {
                            values[item.Index] = item.Text;
                        }
                        job.AddJobParameter(currentSwitch.Text, values);
                    }
                    break;
            }
        }

        private void btnRun_Click(object sender, EventArgs e)
        {
            job.RunJob();
            prgRunning.Visible = true;
            btnRun.Enabled = false;
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            job.AddJobParameter(job.ParamReference[0], new string[1]);
            job.AddJobParameter(job.ParamReference[7], new string[1] { "DAT" });
            job.AddJobParameter(job.ParamReference[54], new string[1] { numSwitchR.Value.ToString() });
            job.AddJobParameter(job.ParamReference[55], new string[1] { numSwitchW.Value.ToString() });
        }

        private void txtSource_EnabledChanged(object sender, EventArgs e)
        {
            lblSource.Enabled = txtSource.Enabled;
            btnSource.Enabled = txtSource.Enabled;
            if (txtSource.Enabled == false) { txtSource.Text = ""; }
        }

        private void txtSource_TextChanged(object sender, EventArgs e)
        {
            job.Source = txtSource.Text;
        }

        private void txtTarget_EnabledChanged(object sender, EventArgs e)
        {
            lblTarget.Enabled = txtTarget.Enabled;
            btnTarget.Enabled = txtTarget.Enabled;
            if (txtTarget.Enabled == false) { txtTarget.Text = ""; }
        }

        private void txtTarget_TextChanged(object sender, EventArgs e)
        {
            job.Target = txtTarget.Text;
        }

        private void txtFiles_TextChanged(object sender, EventArgs e)
        {
            job.Files = txtFiles.Text;
        }

        private void mnuSwitchXDFolders_Click(object sender, EventArgs e)
        {
            DialogResult result = dlgPickFolder.ShowDialog();
            if (result == DialogResult.OK)
            {
                lvwSwitchXD.Items.Add(dlgPickFolder.SelectedPath);
                string[] values = new string[lvwSwitchXD.Items.Count];
                foreach (ListViewItem item in lvwSwitchXD.Items) { values[item.Index] = item.Text; }
                job.AddJobParameter(chkSwitchXD.Text, values);
            }
        }

        private void mnuSwitchXDPatterns_Click(object sender, EventArgs e)
        {
            lvwSwitchXD.Items.Add("New Folder");
            lvwSwitchXD.Focus();
            lvwSwitchXD.Items[lvwSwitchXD.Items.Count - 1].BeginEdit();
        }

        private void mnuSwitchXFFiles_Click(object sender, EventArgs e)
        {
            DialogResult result = dlgPickFile.ShowDialog();
            if (result == DialogResult.OK)
            {
                
                foreach (string file in dlgPickFile.FileNames)
                {
                    lvwSwitchXF.Items.Add(file);
                }
                string[] values = new string[lvwSwitchXF.Items.Count];
                foreach (ListViewItem item in lvwSwitchXF.Items)
                {
                    values[item.Index] = item.Text;
                }
                job.AddJobParameter(chkSwitchXF.Text, values);
            }
        }

        private void mnuSwitchXFPatterns_Click(object sender, EventArgs e)
        {
            lvwSwitchXF.Items.Add("*.*");
            lvwSwitchXF.Focus();
            lvwSwitchXF.Items[lvwSwitchXF.Items.Count - 1].BeginEdit();
        }

        private void switchAttributeSelectors_SelectedAttributesChanged(object sender, EventArgs e)
        {
            AttributeControl.AttributeSelector currentSelector = (AttributeControl.AttributeSelector)sender;
            CheckBox currentSwitch = (CheckBox)this.Controls.Find(currentSelector.Name.Replace("attr", "chk"), true)[0];
            if (currentSelector.AttributeString.Length > 0)
            {
                job.AddJobParameter(currentSwitch.Text, new string[] { currentSelector.AttributeString });
            }
            else
            {
                job.RemoveJobParameter(currentSwitch.Text);
            }
        }

        private void switchAttributeSelectors_EnabledChanged(object sender, EventArgs e)
        {
            AttributeControl.AttributeSelector currentSelector = (AttributeControl.AttributeSelector)sender;
            currentSelector.UnselectAll();
        }

        private void switchListViews_KeyUp(object sender, KeyEventArgs e)
        {
            ListView currentList = (ListView)sender;
            CheckBox currentSwitch = (CheckBox)this.Controls.Find(currentList.Name.Replace("lvw", "chk"), true)[0];
            string[] values = new string[currentList.Items.Count];
            if (e.KeyCode == Keys.Delete & currentList.SelectedItems.Count > 0)
            {
                currentList.Items.Remove(currentList.SelectedItems[0]);
                currentList.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
                if (currentList.Items.Count > 0)
                {
                    foreach (ListViewItem item in currentList.Items) { values[item.Index] = item.Text; }
                    job.AddJobParameter(currentSwitch.Text, values);
                }
                else
                {
                    job.RemoveJobParameter(currentSwitch.Text);
                }
            }
        }

        private void switchListViews_AfterLabelEdit(object sender, LabelEditEventArgs e)
        {
            ListView currentList = (ListView)sender;
            CheckBox currentSwitch = (CheckBox)this.Controls.Find(currentList.Name.Replace("lvw", "chk"), true)[0];
            currentList.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
            string[] values = new string[currentList.Items.Count];
            foreach (ListViewItem item in currentList.Items) { values[item.Index] = item.Text; }
            values[e.Item] = e.Label;
            job.AddJobParameter(currentSwitch.Text, values);
        }

        private void switchListViews_EnabledChanged(object sender, EventArgs e)
        {
            ListView currentList = (ListView)sender;
            CheckBox currentSwitch = (CheckBox)this.Controls.Find(currentList.Name.Replace("lvw", "chk"), true)[0];
            if (currentList.Enabled & currentList.Items.Count > 0)
            {
                string[] values = new string[currentList.Items.Count];
                foreach (ListViewItem item in currentList.Items) { values[item.Index] = item.Text; }
                job.AddJobParameter(currentSwitch.Text, values);
            }
            else if (currentList.Items.Count > 0)
            {
                job.RemoveJobParameter(currentSwitch.Text);
            }
        }

        private void switchNumerics_Enter(object sender, EventArgs e)
        {
            NumericUpDown currentNum = (NumericUpDown)sender;
            currentNum.Select(0, currentNum.Value.ToString().Length);
        }

        private void switchNumerics_ValueChanged(object sender, EventArgs e)
        {
            NumericUpDown currentNum = (NumericUpDown)sender;
            CheckBox currentSwitch = (CheckBox)this.Controls.Find(currentNum.Name.Replace("num", "chk"), true)[0];
            job.AddJobParameter(currentSwitch.Text, new string[1] { currentNum.Value.ToString() });
        }

        private void switchNumerics_EnabledChanged(object sender, EventArgs e)
        {
            NumericUpDown currentNum = (NumericUpDown)sender;
            CheckBox currentSwitch = (CheckBox)this.Controls.Find(currentNum.Name.Replace("num", "chk"), true)[0];
            if (currentNum.Enabled)
            {
                job.AddJobParameter(currentSwitch.Text, new string[1] { currentNum.Value.ToString() });
            }
            else
            {
                job.RemoveJobParameter(currentSwitch.Text);
            }
        }

        private void switchTextBoxes_EnabledChanged(object sender, EventArgs e)
        {
            TextBox currentTextBox = (TextBox)sender;
            if (currentTextBox.Text == string.Empty) { return; }
            switchTextBoxes_TextChanged(currentTextBox, new EventArgs());
        }

        private void switchTextBoxes_TextChanged(object sender, EventArgs e)
        {
            TextBox currentTextBox = (TextBox)sender;
            CheckBox currentCheckBox = (CheckBox)this.Controls.Find(currentTextBox.Name.Replace("txt", "chk"), true)[0];
            if (currentTextBox.Text != string.Empty & currentTextBox.Enabled)
            {
                job.AddJobParameter(currentCheckBox.Text, new string[1] { currentTextBox.Text });
            }
            else
            {
                job.RemoveJobParameter(currentCheckBox.Text);
            }
        }
    }
}
